home *** CD-ROM | disk | FTP | other *** search
- ; Wb-tree File Based Associative String Data Base System.
- ; Copyright (c) 1991, 1992, 1993 Holland Mark Martin
- ;
- ;Permission to use, copy, modify, and distribute this software and its
- ;documentation for educational, research, and non-profit purposes and
- ;without fee is hereby granted, provided that the above copyright
- ;notice appear in all copies and that both that copyright notice and
- ;this permission notice appear in supporting documentation, and that
- ;the name of Holland Mark Martin not be used in advertising or
- ;publicity pertaining to distribution of the software without specific,
- ;written prior consent in each case. Permission to incorporate this
- ;software into commercial products can be obtained from Jonathan
- ;Finger, Holland Mark Martin, 174 Middlesex Turnpike, Burlington, MA,
- ;01803-4467, USA. Holland Mark Martin makes no representations about
- ;the suitability or correctness of this software for any purpose. It
- ;is provided "as is" without express or implied warranty. Holland Mark
- ;Martin is under no obligation to provide any services, by way of
- ;maintenance, update, or otherwise.
-
-
- (require (in-vicinity (program-vicinity) "sys"))
-
- ;;; BT-SCAN scans all keys in the range [key1..key2),
- ;;; performing one of several functions:
- ;;; OPERATION FUNC RESULT
- ;;; ----------- ---------- -----------------------------------------------
- ;;; COUNT-SCAN NIL counts all keys in range
- ;;; COUNT-SCAN given counts all keys in range satisfying FUNC
- ;;; REM-SCAN NIL deletes all keys in range
- ;;; REM-SCAN given deletes all keys in range satisfying FUNC
- ;;; MODIFY-SCAN NIL ARGERR
- ;;; MODIFY-SCAN given updates values for keys in range satisfying FUNC
- ;;; ----------- ---------- -----------------------------------------------
-
- ;;; BT-SCAN returns SUCCESS if scan completed; under any other result code
- ;;; the scan is resumable. The possible results are:
- ;;; NOTPRES meaning the blk-limit was exceeded;
- ;;; RETRYERR meaning FUNC or delete got a RETRYERRR;
- ;;; TERMINATED meaning FUNC asked to terminate the scan;
- ;;; <other error> means FUNC or DELETE encountered this errror.
- ;;;
- ;;; Each block of data is scanned/deleted/modified in a single operation
- ;;; that is, the block is found and locked only once, and only written after
- ;;; all modifications are made. Tho only exception is that MODIFY-SCANs
- ;;; that increase the size of values can cause block splits. Such cases
- ;;; are detected and converted to a PUT plus a NEXT. This has
- ;;; two consequences: data is written out each time a PUT occurs,
- ;;; and it is conceivable that FUNC may be called more than once on the
- ;;; key value that caused the split if a RETRYERR occurs in the PUT.
- ;;; However, SCAN guarantees that only one modification will actually be
- ;;; made in this case (so that one can write INCREMENT-RANGE, for example).
- ;;;
- ;;; FUNC is passed pointers to (copies of) the key and value,
- ;;; plus one user argument:
- ;;; (FUNC keystr klen vstr vlen extra-arg)
- ;;; FUNC is expected to return either: SUCCESS for DELETE/COUNT,
- ;;; NOTPRES/NOTDONE for SKIP (ie, DONT DELETE/COUNT), or
- ;;; any other code to terminate the scan resumably at the current point.
- ;;; For MODIFY-SCAN, if changing the value, the new value length is returned.
- ;;; Except for the case mentioned above, the caller can depend on FUNC
- ;;; being called exactly once for each key value in the specified range,
- ;;; and only on those values.
- ;;;
- ;;; If key2<=key1 no scan will occur (even if key1 is found).
- ;;; To make possible bounded-time operation bt-scan will
- ;;; access at most BLK-LIMIT blocks at a time; if you dont care,
- ;;; give it -1 for BLK-LIMIT.
- ;;;
- ;;; The number of keys deleted/counted/modified is returned in the SKEY-COUNT
- ;;; field of respkt; the key to resume at is returned in KEY-STR (***which
- ;;; therefore needs to be 256 bytes long***); and the new key length
- ;;; is returned in SKEY-LEN. If returns SUCCESS, SKEY-LEN is zero.
- ;;; NOTE that SKEY-COUNT is cumulative, so the caller need to init it to 0
- ;;; when starting a new scan.
- ;;;
- ;;; ***WARNING*** when BT-SCAN returns other than SUCCESS,
- ;;; it MODIFIES the KEY1 string
- ;;; so that the string args are correctly set up for the next call
- ;;; (the returned value is the new length for KEY1).
- ;;; Therefore, KEY-STR MUST BE A MAXIMUM-LENGTH STRING [!!]
-
- ;;; changes: 11/12: merged DELETE and SCAN into one;
- ;;; changed FUNC calling protocol to copy value
- ;;; 11/18: fixed bug where SCAN always used ACCWRITE (!oops!)
- ;;; added MODIFY SCAN
- ;;; 12/01: fixed compares on OPERATION to use EQ? instead of =
-
- ;;; AGJ - bt-scan modified so that it copies the ent when
- ;;; COUNT-SCANning it. This allows nested SCANs and BTree refs in
- ;;; func without contention.
-
- (define (bt-scan han operation key-str k-len key2-str k2-len
- func long-tab respkt blk-limit)
- (define pkt (make-vector PKT-SIZE))
- (define opkt (make-vector PKT-SIZE))
- (define ent #f)
- (define vstr (make-string 256))
- (define accmode (if (eq? operation COUNT-SCAN) ACCREAD ACCWRITE))
- (define result SUCCESS)
- ; (fprintf diagout "bt-scan %d:%ld %.*s::%.*s\\n"
- ; (HAN-SEG han) (HAN-ID han)
- ; (max 0 k-len) key-str (max 0 k2-len) key2-str)
- (cond
- ((< k-len -2)
- (fprintf diagout ">>>>ERROR<<<< bt-scan: bad length string1 %d\\n" k-len)
- ARGERR)
- ((< k2-len -1)
- (fprintf diagout ">>>>ERROR<<<< bt-scan: bad length string2 %d\\n" k2-len)
- ARGERR)
- ((and (eq? operation MODIFY-SCAN) (not func))
- (fprintf diagout ">>>>ERROR<<<< bt-scan: MODIFY-SCAN requires func be specified\\n")
- ARGERR)
- (else
- (set! ent (chain-find-ent han accmode key-str k-len pkt))
- (cond
- ((and ent (blk-find-pos (ENT-BLK ent) key2-str k2-len opkt))
- (cond
- ((eq? operation COUNT-SCAN) ;here we deal with a copy of ent
- (let ((nent (allocate-ent))) ;to avoid ACCREAD contention.
- (ent-copy! nent ent)
- (release-ent! ent accmode) ;accmode = ACCREAD here.
- (set! result (chain-scan nent operation pkt opkt key-str
- func long-tab vstr respkt (HAN-WCB han)))
- (recycle-ent! nent)))
- (else
- (set! result (chain-scan ent operation pkt opkt key-str func long-tab vstr respkt (HAN-WCB han)))
- (release-ent! ent accmode)
- (cond ((> result 0) ; check for MODIFY special case
- (set! result (bt-put han key-str (SKEY-LEN respkt) vstr result))
- (cond ((= result SUCCESS)
- (SET-SKEY-COUNT! respkt (+ (SKEY-COUNT respkt) 1))
- (SET-SKEY-LEN! respkt (increment-string key-str (SKEY-LEN respkt) 256))
- (set! result NOTPRES)))))))
- (if (and (= result NOTPRES) ; ie, is there more to do?
- (not (= 0 blk-limit)))
- (bt-scan han operation key-str (SKEY-LEN respkt)
- key2-str k2-len func long-tab respkt (- blk-limit 1))
- result))
- (else
- (if ent (release-ent! ent accmode))
- (set! rem-fct (+ 1 rem-fct))
- UNKERR)))))
-
- ;; this function increments a string lexicographically
- (define (increment-string str len maxlen)
- (cond ((< len maxlen)
- (string-set! str len (integer->char 0))
- (+ len 1))
- (else
- (let ((oldval (char->integer (string-ref str (- len 1)))))
- (string-set! str (- len 1) (integer->char (+ 1 oldval)))
- len))))
-
- ;;; Each call to CHAIN-SCAN scans
- ;;; all the keys within the specified range WITHIN block ENT.
- ;;; If the scan actually reaches the end of range, it sets SKEY-LEN=0
- ;;; and returns SUCCESS. If there's more to the range,
- ;;; it sets KEY-STR to the key to continue deleting
- ;;; from (ie, the split key of ENT), SKEY-LEN to its length, and
- ;;; returns NOTPRES (NOTDONE). The caller must then call CHAIN-FIND
- ;;; to find the START and END keys and call again.
-
- (define (chain-scan ent operation pkt opkt key-str func long-tab vstr respkt wcb)
- (let ((blk (ENT-BLK ent))
- (result SUCCESS))
- ; check for special case of
- ; unconditional delete of entire block
- (cond ((and (eq? operation REM-SCAN)
- (not func)
- (> (MATCH-POS opkt) (MATCH-POS pkt))
- (= (MATCH-POS pkt) BLK-DATA-START)
- (at-split-key-pos? blk (MATCH-POS opkt)))
- ;; (fprintf diagout "CHAIN-SCAN: Udelete(blk %d)\\n" (BLK-ID blk))
- (let ((key-len (recon-this-key blk (MATCH-POS opkt) ; delete data
- key-str 0 256)))
- (substring-move! key-str 0 key-len blk (+ BLK-DATA-START 2))
- (SET-FIELD-LEN! blk (+ BLK-DATA-START 1) key-len)
- (BLK-SET-END! blk (+ BLK-DATA-START 2 key-len)))
- (SET-SKEY-COUNT! respkt (+ (SKEY-COUNT respkt) 1)) ; estimate only!
- (set! rem-ct (+ 1 rem-ct))
- (ENT-SET-DTY! ent #t)
- (SET-MATCH-POS! opkt BLK-DATA-START))
- (else ; else scan/delete/modify a subrange
- (let ((oldct (SKEY-COUNT respkt))
- (ckstr (make-string 256))
- (clen #f))
- (if func
- (set! clen (recon-this-key blk (MATCH-POS pkt) ckstr 0 256)))
- (SET-MATCH-TYPE! pkt MATCH) ; by definition
- (set! result
- (scan-loop (ENT-BLK ent) operation pkt opkt func long-tab respkt
- ckstr clen vstr (SEG-BSIZ (ENT-SEG ent))))
- (if (and (not (eq? operation COUNT-SCAN))
- (> (SKEY-COUNT respkt) oldct))
- (ENT-SET-DTY! ent #t)))
- ))
- ; delete blk if empty
- (if (and (eq? operation REM-SCAN)
- (BLK-EMPTY? blk)
- (not (END-OF-CHAIN? blk)))
- (blk-delete ent)
- (if (ENT-DTY? ent)
- (if (or (and (eq? operation REM-SCAN)
- (or (WCB-SAR? wcb)
- (> (BLK-LEVEL blk) LEAF)))
- (and (eq? operation MODIFY-SCAN) (WCB-SAP? wcb)))
- (ent-write ent))))
- ;further scanning needed?
- (cond ((not (= result SUCCESS))
- (SET-SKEY-LEN! respkt (recon-this-key blk (MATCH-POS pkt)
- key-str 0 256))
- ;; (fprintf diagout "CHAIN-SCAN: returning result %d\\n" result)
- result)
- ((and (eq? (MATCH-TYPE opkt) PASTEND)
- (not (END-OF-CHAIN? blk)))
- (SET-SKEY-LEN! respkt (recon-this-key blk (MATCH-POS pkt)
- key-str 0 256))
- ;; (fprintf diagout "CHAIN-SCAN: new starting key len=%d\\n" (SKEY-LEN respkt))
- NOTPRES)
- (else
- (SET-SKEY-LEN! respkt 0)
- SUCCESS)) ))
-
- ;; SCAN-LOOP returns SUCCESS if it reaches the end of the range,
- ;; else an ERROR code if terminated before that point, either
- ;; by an error or by FUNC returning TERMINATED.
- ;; SCAN-LOOP returns a value>0 to signal the case of
- ;; a MODIFY that requires a block-split. That value is the
- ;; length of the new value (which must be >0 to have caused an
- ;; increase in block size). SCAN-LOOP NEVER returns NOTPRES.
- ;; Note that (MATCH-POS pkt) is always the current scan point.
-
- (define (scan-loop blk operation pkt opkt func long-tab respkt
- ckstr clen vstr blksize)
- ;; (fprintf diagout "SCAN-LOOP called: blk %d pos %d\\n" (blk-id blk) (MATCH-POS pkt))
- (if (> (MATCH-POS opkt) (MATCH-POS pkt))
- (let ((old-bend (BLK-END blk))
- (next-pos (NEXT-CNVPAIR blk (MATCH-POS pkt)))
- (result SUCCESS))
- (if func
- (let* ((vpos (next-field blk (+ 1 (MATCH-POS pkt))))
- (vlen (FIELD-LEN blk vpos)))
- (substring-move! blk (+ vpos 1) (+ vpos vlen 1) vstr 0)
- (set! result (func ckstr clen vstr vlen long-tab))))
- (cond ((>= result SUCCESS) ; ie, if (= result SUCCESS)
- (cond ((eq? operation REM-SCAN)
- (blk-remove-key-and-val blk (MATCH-POS pkt) blksize)
- (SET-SKEY-COUNT! respkt (+ (SKEY-COUNT respkt) 1))
- (set! rem-ct (+ 1 rem-ct))
- (cond
- ((= (MATCH-POS opkt) next-pos)
- (SET-MATCH-POS! opkt (MATCH-POS pkt)))
- (else
- (SET-MATCH-POS! opkt (- (MATCH-POS opkt)
- (- old-bend (BLK-END blk))))))
- (set! next-pos (MATCH-POS pkt)))
- ((eq? operation COUNT-SCAN)
- (SET-SKEY-COUNT! respkt (+ (SKEY-COUNT respkt) 1))
- (SET-MATCH-POS! pkt next-pos))
- ((change-existing-value blk (MATCH-POS pkt)
- ckstr clen vstr result blksize)
- (SET-SKEY-COUNT! respkt (+ (SKEY-COUNT respkt) 1))
- (set! next-pos (- next-pos (- old-bend (BLK-END blk))))
- (SET-MATCH-POS! opkt (- (MATCH-POS opkt)
- (- old-bend (BLK-END blk))))
- (SET-MATCH-POS! pkt next-pos)
- (set! result SUCCESS))
- (else
- (fprintf diagout "ScAN-LOOP: hit modify special case\\n"))
- ))
- ((= result NOTPRES) ; not deleting, just advance scan ptr
- (SET-MATCH-POS! pkt next-pos)
- ))
- (cond ((or (= result SUCCESS) (= result NOTPRES))
- (cond (func ; update key to pass to FUNC
- (set! clen (+ (field-len blk next-pos)
- (field-len blk (+ 1 next-pos))))
- (substring-move! blk (+ next-pos 2)
- (+ next-pos 2 (field-len blk (+ 1 next-pos)))
- ckstr (field-len blk next-pos))
- ))
- (scan-loop blk operation pkt opkt func long-tab respkt
- ckstr clen vstr blksize))
- (else result)))
- SUCCESS))
-